﻿#region copyright
//<copyright>
// Copyright(C) 2012 TrackerRealm Corporation
// This file is part of the open source project - Jazz. http://jazz.codeplex.com
// 
// Jazz is open software: you can redistribute it and/or modify it 
// under the terms of the GNU Affero General Public License (AGPL) as published by 
// the Free Software Foundation, version 3 of the License.
// 
// Jazz is distributed in the hope that it will be useful, 
// but WITHOUT ANY WARRANTY; without even the implied warranty 
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
//  See the GNU Affero General Public License (AGPL) for more details.
// 
// You should have received a copy of the GNU General Public 
// License along with Jazz.  If not, see <http://www.gnu.org/licenses/>.
//
// REMOVAL OF THIS NOTICE IS VIOLATION OF THE COPYRIGHT. 
//</copyright>
#endregion
#region usings
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Newtonsoft.Json;
using TrackerRealm.Jazz.Client;
using Newtonsoft.Json.Converters;
using TrackerRealm.Jazz.Client.Services;
#endregion

namespace TrackerRealm.Jazz.Json
{
    /// <summary>
    /// 
    /// </summary>
    public class JazzBaseFieldSerializer: JazzSerializer
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="converters"></param>
        /// <param name="service"></param>
        internal JazzBaseFieldSerializer(IEnumerable<JsonConverter> converters, SerializerService service)
        :base(converters, service, false)
        {
        }
    }
    #region JazzSerializer
    /// <summary>
    /// Provides the ability to serialize and deserialize Jazz objects using JSON.
    /// </summary>
    public class JazzSerializer
    {
        //
        // Factory Methods
        //
        #region CreateBaseSerializer
        /// <summary>
        /// Create a serializer that is used to serialize only the 'Base' of the Jazz object.
        /// </summary>
        /// <returns></returns>
        public static JazzBaseFieldSerializer CreateSerializerForBaseFieldsOnly()
        {
            return new JazzBaseFieldSerializer(null, null);
        }
        #endregion
        #region CreateSerializer
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public static JazzSerializer CreateSerializer()
        {
            return new JazzSerializer(null, null);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="converters"></param>
        /// <returns></returns>
        public static JazzSerializer CreateSerializer(IEnumerable<JsonConverter> converters)
        {
            return new JazzSerializer(converters, null);
        }
        #endregion
        #region  private fields
        private JsonSerializerSettings settings;
        private SerializerService service;
        /// <summary>
        /// 
        /// </summary>
        protected bool isFullConverter = true;
        #endregion
        //
        // Constructors
        //
        #region Constructors
        /// <summary>
        /// constructor
        /// </summary>
        public JazzSerializer()
        :this(null, null)
        {
        }
        /// <summary>
        /// constructor
        /// </summary>
        /// <param name="converters"></param>
        /// <param name="service"></param>
        protected JazzSerializer(IEnumerable<JsonConverter> converters, SerializerService service)
        :this(converters, service, true)
        {
        }
        /// <summary>
        /// constructor
        /// </summary>
        /// <param name="converters"></param>
        /// <param name="service"></param>
        /// <param name="isFullConverter"></param>
        protected JazzSerializer(IEnumerable<JsonConverter> converters, SerializerService service, bool isFullConverter)
        {
            this.isFullConverter = isFullConverter;
            if (service == null)
            {
                this.service = new SerializerService();
            }
            else
                this.service = service;
            jObjectReferenceConverter jObjConverter = new jObjectReferenceConverter(this.service);
            JsonConverter[] builtinConverters =
                    new JsonConverter[] { 
                            new jObjectConverter(this.service, this.isFullConverter),
                            jObjConverter,
                            new jStateConverter(),
                            new jTimestampConverter(),
                            new IsoDateTimeConverter()
                };
            IEnumerable<JsonConverter> cvrtrs = builtinConverters;
            if (converters != null)
            {
                cvrtrs = builtinConverters.Union(converters);
            }
            JazzContractResolver jcr = new JazzContractResolver(false, jObjConverter);
            this.settings = new JsonSerializerSettings()
            {
                ContractResolver = jcr,
                Converters = cvrtrs.ToArray(),
            };
            jcr.DefaultMembersSearchFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;
        }
        #endregion
        //
        // instance methods and properties
        //
        #region service
        /// <summary>
        /// 
        /// </summary>
        public SerializerService Service
        {
            get { return this.service; }
        }
        #endregion
        #region Serialize
        /// <summary>
        /// 
        /// </summary>
        /// <param name="jazzObject"></param>
        /// <returns></returns>
        public string Serialize(jObject jazzObject)
        {
            JazzContainer jContainer =
                   new JazzContainer() { JazzObject = jazzObject };
            //this.service.Clear();
            string js = JsonConvert.SerializeObject(jContainer, Newtonsoft.Json.Formatting.Indented, settings);
            return js;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="jazzObjects"></param>
        /// <returns></returns>
        public  string Serialize(IEnumerable<jObject> jazzObjects)
        {
            IEnumerable<JazzContainer> jContainers =
                   jazzObjects.Select<jObject, JazzContainer>(j => new JazzContainer() { JazzObject = j });
            List<JazzContainer> list = new List<JazzContainer>();
            list.AddRange(jContainers);
            //this.service.Clear();
            string js = JsonConvert.SerializeObject(list, Newtonsoft.Json.Formatting.Indented, settings);
            return js;
        }
        #endregion
        #region Deserialize
        /// <summary>
        /// 
        /// </summary>
        /// <param name="jsonText"></param>
        /// <returns></returns>
        public IEnumerable<jObject> Deserialize(string jsonText)
        {
            //this.service.Clear();
            List<JazzContainer> myRestoredObj = JsonConvert.DeserializeObject<List<JazzContainer>>(jsonText, settings);
            return myRestoredObj.Select<JazzContainer, jObject>(c => c.JazzObject);
        }
        #endregion
        #region NotDeserialized
        /// <summary>
        /// After 'deserializing' this method should return an empty array.
        /// <para>Any members in the array are IDs of jazz objects that the deserializer created; however
        /// it found no JSON data to populate the object.</para>
        /// </summary>
        /// <returns></returns>
        public Guid[] NotDeserialized()
        {
            return this.service.UnInitialized();
        }
        #endregion
        #region Clear
        /// <summary>
        /// Clears the internal cache of 'jObject' references.
        /// </summary>
        public void Clear()
        {
            this.service.Clear();
        }
        #endregion
        internal JsonSerializerSettings Settings
        {
            get { return this.settings; }
        }
    }
    #endregion
}
